package edu.cicese.tm;

import com.jsurf.InterestPoint;
import com.jsurf.Points;
import com.jsurf.SurfUtil;
import edu.cicese.graphics.JMultiLineToolTip;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.JToolTip;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * Created by: Eduardo Quintana Contreras
 * Date: 1/03/11
 * Time: 12:50 PM
 */
public class FrameTagCreation extends JFrame {
	private FrameSpace frmSpace;
	private FrameTagCreation frmTagCreation;
	private JPanel pImgLoaded;

	private BufferedImage originalLoadedImage, cachedLoadedImage, cachedMatchedImage, bufferedPolygon;
	private Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();

	private String spaceID;
	private Map<String, List<Match>> matchingMatrix;
	private List<Match> matches;

	private List<InterestPoint> imgQueriedPoints;
	private List<InterestPoint> imgQueriedPointsInArea = new ArrayList<InterestPoint>();

	private boolean imageLoaded = false;
	private int selectedIndex;
	private int numberOfBestMatches = 10;

	private JToggleButton tbnAvailability;

	// Matches
	private JPanel pMatch;
	private JButton btnShowMatches;
	private JButton[] btnMatches;
	private JCheckBox[] chbMatches;
	private BestMatch[] bestMatches;
	private boolean firstMatches = true;

	public boolean mainFrameResized = false;
	private int qclickCount = 0;
	private List<Integer> qxCoord = new ArrayList<Integer>();

	private List<Integer> qyCoord = new ArrayList<Integer>();
	private int mclickCount = 0;
	private List<Integer> mxCoord = new ArrayList<Integer>();

	private List<Integer> myCoord = new ArrayList<Integer>();
	private final int QUERY_WIDTH = 550;
	private final int QUERY_HEIGHT = 412;
	private final int MATCH_WIDTH = 400;

	private final int MATCH_HEIGHT = 300;
	private final float QUERY_SCALE = 0.6875f;
	private final float MATCH_SCALE = 0.5f;

	//TAG
	public String annotation;
	public String date;

	public JCheckBox chbAnnotation, chbAudio, chbVideo;

	//TODO: Test only, REMOVE
	public FrameTagCreation() {
		frmTagCreation = this;
		/*try {
					 UIManager.setLookAndFeel(new PlasticXPLookAndFeel());
					 UIManager.put("Panel.background", Color.WHITE);
					 UIManager.put("ProgressBar.selectionBackground", Color.WHITE);
				 } catch (UnsupportedLookAndFeelException e) {
					 e.printStackTrace();
				 }*/
		openAudioDialog();
	}

	public FrameTagCreation(FrameSpace frm) {
		frmSpace = frm;
		frmTagCreation = this;
		setIconImage(new ImageIcon(getClass().getClassLoader().getResource("icons/icon.png")).getImage());
		setTitle("Creación de etiqueta");
		setLayout(null);
		setSize(565, 451);
		setResizable(false);

		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				closeWindow(false);
			}
		});
		initComponents();

		/*	try {
								UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
							}
							catch(Exception e) { }*/
		//	setVisible(true);
		/*
							try {
								BufferedImage tempImg = (BufferedImage)createImage(600, 450);
								Graphics2D tempG = (Graphics2D)tempImg.getGraphics();
								tempG.drawImage(ImageIO.read(new File("D:\\Tests\\imgs\\Espacios\\noImage.jpg")), 0, 0, null);
								Stroke drawingStroke = new BasicStroke(3, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{9}, 0);
								tempG.setStroke(drawingStroke);
								tempG.setColor(Color.LIGHT_GRAY);
								tempG.drawRoundRect(0, 0, 598, 448, 15, 15);
								tempG.dispose();
								cachedQueriedImage = tempImg;

							} catch (Exception e) { e.printStackTrace(); }*/
	}

	//! Creates GUI components
	private void initComponents() {
		// Image queried
		pImgLoaded = new JPanel() {
			public void paintComponent(Graphics g) {
				super.paintComponent(g);
				g.drawImage(cachedLoadedImage, 0, 0, null);
			}
			public JToolTip createToolTip() {
				return new JMultiLineToolTip();
			}
		};
		pImgLoaded.setBounds(5, 5, QUERY_WIDTH, QUERY_HEIGHT);
		pImgLoaded.setToolTipText("-Clic izquierdo para agregar vértice\n-Clic derecho para terminar");
		pImgLoaded.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				int x, y;
				if (imageLoaded) {
					x = e.getX();
					y = e.getY();
					if (!(e.isMetaDown() && qclickCount < 2)) {
						qxCoord.add(x);
						qyCoord.add(y);
						qclickCount++;
						if (e.isMetaDown() && qclickCount > 2) {
							drawPolygon(x, y, true);
							//Get the best matches
							matches = SurfUtil.getBestMatches(imgQueriedPointsInArea);
							showBestMatches();
							if (!mainFrameResized) {
								mainFrameResized = true;
								setSize(1070, 451);
								Point location = getLocation();
								setLocation((screen.width - getSize().width) / 2, (int) location.getY());
							}
						}
					}
				}
			}
		});
		pImgLoaded.addMouseMotionListener(new MouseMotionAdapter() {
			public void mouseMoved(MouseEvent e) {
				if (qclickCount > 0) {
					drawPolygon(e.getX(), e.getY(), false);
				}
			}
		});
		cachedLoadedImage = (BufferedImage)createImage(QUERY_WIDTH, QUERY_HEIGHT);

		// Matches
		Border btnBorder = new LineBorder(Color.WHITE, 1);
		btnMatches = new JButton[numberOfBestMatches];
		chbMatches = new JCheckBox[numberOfBestMatches];
		boolean matchVisible = true;
		for (int i = 0, posX = 560; i < numberOfBestMatches; i++, posX += 100) {
			if (i == 5) {
				posX = 560;
				matchVisible = false;
			}
			final int tempIndex = i;
			btnMatches[i] = new JButton();
			btnMatches[i].setVisible(matchVisible);
			btnMatches[i].setBounds(posX, 320, 100, 75);
			btnMatches[i].setBorder(btnBorder);
			btnMatches[i].setContentAreaFilled(false);
			btnMatches[i].setFocusPainted(false);
			btnMatches[i].setFont(new Font("Segoe UI", Font.BOLD, 23));
			btnMatches[i].setForeground(Color.WHITE);
			btnMatches[i].setHorizontalTextPosition(SwingConstants.CENTER);
			btnMatches[i].addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					selectedIndex = tempIndex;
					drawBestMatch();
				}
			});
			btnMatches[i].addMouseListener(new MouseAdapter () {
				public void mouseEntered(MouseEvent e) {
					btnMatches[tempIndex].setText("Ampliar");
				}
				public void mouseExited(MouseEvent e) {
					btnMatches[tempIndex].setText("");
				}
			});

			chbMatches[i] = new JCheckBox();
			chbMatches[i].setVisible(matchVisible);
			chbMatches[i].setBackground(Color.WHITE);
			SwingUtil.addCheckBox(chbMatches[i], posX + 46, 395, 20, 20, null, getContentPane());

			getContentPane().add(btnMatches[i]);


		}

		// Match panel
		pMatch = new JPanel() {
			public void paintComponent(Graphics g) {
				super.paintComponent(g);
				g.drawImage(cachedMatchedImage, 0, 0, null);
			}
		};
		pMatch.setSize(MATCH_WIDTH, MATCH_HEIGHT);
		pMatch.setLocation(620, 12);
		pMatch.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				chbMatches[selectedIndex].setSelected(true);
				int x = e.getX(), y = e.getY();
				mxCoord.add(x);
				myCoord.add(y);
				mclickCount++;
				if (e.isMetaDown()){
					mdrawPolygon(x, y, true);
					bestMatches[selectedIndex].hasAreaSelected = true;
				}
			}
			public void mouseClicked(MouseEvent e) {
			}
		});
		pMatch.addMouseMotionListener(new MouseMotionAdapter() {
			public void mouseMoved(MouseEvent e) {
				if (mclickCount > 0) {
					mdrawPolygon(e.getX(), e.getY(), false);
				}
			}
		});
		cachedMatchedImage = (BufferedImage)createImage(MATCH_WIDTH, MATCH_HEIGHT);

		btnShowMatches = new JButton();
		addButton(btnShowMatches, 1025, 280, 32, 32, "Mostrar más coincidencias", "expand", null, getContentPane());
		btnShowMatches.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionShowMatches();
			}
		});

		// Info labels
//		lblMatchName = new JLabel();
//		lblMatchName.setLocation(1171, 7);
//		lblMatchName.setFont(new Font("Calibri", Font.BOLD, 20));
//		lblMatchName.setForeground(Color.BLACK);
//		lblMatchName.setSize(80, 20);
//
//		lblMatchedPoints = new JLabel();
//		lblMatchedPoints.setLocation(1171, 27);
//		lblMatchedPoints.setFont(new Font("Calibri", Font.BOLD, 20));
//		lblMatchedPoints.setForeground(Color.RED);
//		lblMatchedPoints.setSize(80, 20);
//
//		lblPointsInArea = new JLabel();
//		lblPointsInArea.setLocation(1171, 47);
//		lblPointsInArea.setFont(new Font("Calibri", Font.BOLD, 20));
//		lblPointsInArea.setForeground(Color.ORANGE);
//		lblPointsInArea.setSize(80, 20);

		JButton btnBack = new JButton();
		addButton(btnBack, 560, 5, 32, 32, "Regresar (sin guardar cambios)", "back", null, getContentPane());
		btnBack.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionBack();
			}
		});

		JButton btnSave = new JButton();
		addButton(btnSave, 560, 62, 32, 32, "Guardar cambios", "save", null, getContentPane());
		btnSave.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionSave();
			}
		});

		JButton btnAnnotation = new JButton();
		addButton(btnAnnotation, 560, 99, 32, 32, "Editar anotación", "edit", null, getContentPane());
		btnAnnotation.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				openAnnotationDialog();
			}
		});

		JButton btnAudio = new JButton();
		addButton(btnAudio, 560, 136, 32, 32, "Editar clip de audio", "audio", null, getContentPane());
		btnAudio.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				openAudioDialog();
			}
		});

		JButton btnVideo = new JButton();
		addButton(btnVideo, 560, 173, 32, 32, "Editar clip de video", "video", null, getContentPane());
		btnVideo.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {

			}
		});

		JButton btnCalendar = new JButton();
		addButton(btnCalendar, 560, 210, 32, 32, "Especificar fecha y hora", "calendar", null, getContentPane());
		btnCalendar.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				openCalendarDialog();
			}
		});

		tbnAvailability = new JToggleButton();
		tbnAvailability.setToolTipText("Deshabilitar");
		tbnAvailability.setSelectedIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/disabled.png")));
		tbnAvailability.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/enabled.png")));
		tbnAvailability.setBounds(560, 247, 32, 32);
		tbnAvailability.setContentAreaFilled(false);
		tbnAvailability.setBorder(new EmptyBorder(0, 0, 0, 0));
		tbnAvailability.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				enableTag();
			}
		});
		getContentPane().add(tbnAvailability);

		chbAnnotation = new JCheckBox();
		SwingUtil.addCheckBox(chbAnnotation, 594, 105, 20, 20, null, getContentPane());
		chbAudio = new JCheckBox();
		SwingUtil.addCheckBox(chbAudio, 594, 142, 20, 20, null, getContentPane());
		chbVideo = new JCheckBox();
		SwingUtil.addCheckBox(chbVideo, 594, 179, 20, 20, null, getContentPane());



		// Maximum number of points to save

//        txtMaxPoints.setLocation(615, 305);
//        txtMaxPoints.setSize(70, 25);

		getContentPane().add(pImgLoaded);
		getContentPane().add(pMatch);
//		getContentPane().add(lblMatchName);
//		getContentPane().add(lblMatchedPoints);
//		getContentPane().add(lblPointsInArea);
//		getContentPane().add(txtMaxPoints);
	}

	//! Sets the initial values
	public void setValues(String spaceID) {
		set(spaceID);

		// Clean values
		chbAnnotation.setSelected(false);
		chbAudio.setSelected(false);
		chbVideo.setSelected(false);
		clearChbMatches();
		annotation = "";
//		audioClip = false;
		date = "{\"type\":0}";
	}

	public void setValues(String spaceID, String tagID) {
		set(spaceID);

		String filename = Utilities.getTagDir(spaceID) + Utilities.tagSize + "\\" + tagID + "\\" + tagID;

		// load tag annotation
		annotation = new File(filename + ".txt").exists() ? Utilities.loadString(filename + ".txt") : "";
		annotation = annotation == null ? "" : annotation;
//		System.out.println(annotation + "..." + filename);
		chbAnnotation.setSelected(!annotation.equals(""));

		// load audio clip
		if (new File(filename + ".wav").exists()) {
			DialogAudio.tmpAudioSaved = true;
			Utilities.copyFile(filename + ".wav", Utilities.spaceDir + "tmpAudio.wav");
			chbAudio.setSelected(true);
		}
		else {
			chbAudio.setSelected(false);
		}
		/*// check for audio clip
		chbAudio.setSelected(new File(filename + ".wav").exists());*/

		// check for video clip
		chbVideo.setSelected(new File(filename + ".3gp").exists());

		// clear matches
		clearChbMatches();

		// load tag date
		JSONObject joDate = null;
		if (new File(filename + ".date").exists()) {
			date = Utilities.loadString(filename + ".date");
			joDate = (JSONObject) JSONValue.parse(date);
		}
		if (joDate == null) {
			date = "";
			joDate = new JSONObject();
		}

		// Check tag availability
//		JSONObject joDate = (JSONObject) JSONValue.parse(date);
		/*if (*//*joDate != null && *//*joDate.get("enabled") != null) {
			initialStateEnabled = Boolean.valueOf(joDate.get("enabled").toString());
		}
		else {
			initialStateEnabled = true;
		}
		tbnAvailability.setSelected(!initialStateEnabled);
		enableTag();*/
	}

	private void set(String spaceID) {
		this.spaceID = spaceID;
		// load the spaceID matching matrix
		//TODO: Check when the matching matrix is null
		matchingMatrix = SurfUtil.loadMatchingMatrix(spaceID);

		qclickCount = 0;
		qxCoord.clear();
		qyCoord.clear();

		firstMatches = false;
		actionShowMatches();
	}

	//! Closes the window and brings back the spaceID window
	private void closeWindow(boolean saveOrBack) {
		if (saveOrBack) {
			setVisible(false);
		}
		frmSpace.setVisible(true);
		frmSpace.toFront();
	}

	//! Called when the Back button is clicked
	private void actionBack() {
		closeWindow(true);
	}

	//! Called when the Save button is clicked
	private void actionSave() {
		if (!chbAnnotation.isSelected() && !chbAudio.isSelected()) {
			if (JOptionPane.showConfirmDialog(this, "La etiqueta a crear se encuentra vacía, ¿desea continuar?",
					"Etiqueta vacía", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
					new ImageIcon(getClass().getClassLoader().getResource("icons/warning.png")))
					== JOptionPane.YES_OPTION) {
				saveTag();
			}
			return;
		}
		saveTag();
	}

	//! Draws a requested image and loads its interest points and best matches, according to the matching matrix
	public void drawImageQueried(String imgID) {
		// Check if the poi file exists
		try {
			File poiFile = new File(Utilities.getPoiDir(spaceID) + imgID + ".poi");

			if (poiFile.exists()) {
//				BufferedImage img = ImageIO.read(new File(Utilities.getImgDir(spaceID) + "640x480\\" + imgID + ".jpg"));
				BufferedImage img = ImageIO.read(new File(Utilities.getImgDir(spaceID) + "800x600\\" + imgID + ".jpg"));
				Graphics2D g2d = (Graphics2D)pImgLoaded.getGraphics();
				BufferedImage tempImg = (BufferedImage)createImage(QUERY_WIDTH, QUERY_HEIGHT);
				Graphics2D tempG = (Graphics2D)tempImg.getGraphics();
				tempG.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
				tempG.drawImage(img, 0, 0, QUERY_WIDTH, QUERY_HEIGHT, null);
				tempG.dispose();
				g2d.drawImage(tempImg, null, 0, 0);
				originalLoadedImage = cachedLoadedImage = tempImg;

				// Load the interest points
				imgQueriedPoints = SurfUtil.loadPointsFromFile(poiFile.getCanonicalPath()).points;
				imageLoaded = true;
				SurfUtil.loadBestMatches(spaceID, imgID, matchingMatrix, numberOfBestMatches);

//				clearChbMatches();
			}
			else {
				System.out.println("No existe el archivo .poi correspondiente: " + Utilities.getPoiDir(spaceID) + imgID + ".poi");
			}
		} catch (Exception e) { e.printStackTrace(); }
	}

	//! Shows the best matches according to the matching matrix and the area selected
	@SuppressWarnings({"ConstantConditions"})
	private void showBestMatches() {
		bestMatches = new BestMatch[numberOfBestMatches];
		hideMatches();
		for (int i = 0; i < numberOfBestMatches && i < matches.size(); i++) {
			String imagePath = Utilities.getImgDir(spaceID) + "480x360\\" +  matches.get(i).filename + ".jpg";
			btnMatches[i].setIcon(new ImageIcon(new ImageIcon(imagePath).getImage().getScaledInstance(100, 75, Image.SCALE_AREA_AVERAGING)));
			btnMatches[i].setVisible(true);
			chbMatches[i].setVisible(true);
			try {
//				File imgFile = new File(Utilities.imgDir + spaceID + "\\640x480\\" +  matches.get(i).filename + ".jpg");
//				Image image = new ImageIcon(imgFile.getCanonicalPath()).getImage().getScaledInstance(480, 360, Image.SCALE_AREA_AVERAGING);
				Image image = new ImageIcon(imagePath).getImage();
				bestMatches[i] = new BestMatch(matches.get(i).filename, matches.get(i).points, image);
//				getPointsInPolygon(match.imgPoints, match.pointsInArea, polygon, MATCH_SCALE);

				bestMatches[i].pointsInArea.addAll(getPointsInPolygon(matches.get(i).points, bestMatches[i].imgPoints));
//				bestMatches[i].pointsInArea.addAll(matches.get(i).points);
				//Have to access 'image'..  to initialize it?.. Who cares!, it works.
				image.getWidth(null);
			} catch (Exception e) { e.printStackTrace(); }
		}
		selectedIndex = 0;

		drawBestMatch();
		clearChbMatches();
	}

	//! Hides all the best matches (buttons and check boxes)
	private void hideMatches() {
		for (JButton btn : btnMatches) {
			btn.setVisible(false);
		}
		for (JCheckBox chb : chbMatches) {
			chb.setVisible(false);
		}
	}

	//! Draws a matching image, with its selected area
	private void drawBestMatch() {
		BestMatch match = bestMatches[selectedIndex];
		if (match != null) {
			if (!match.hasAreaSelected) {
				int minX, minY, maxX, maxY;

				Graphics2D g2d = (Graphics2D)pMatch.getGraphics();
				BufferedImage tempImg = (BufferedImage)createImage(MATCH_WIDTH, MATCH_HEIGHT);
				Graphics2D tempG = (Graphics2D)tempImg.getGraphics();

				minX = 800;
				minY = 600;
				maxX = 0;
				maxY = 0;
				tempG.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
				tempG.drawImage(match.image, 0, 0, MATCH_WIDTH, MATCH_HEIGHT, null);
				tempG.setColor(new Color(250, 125, 0, 150));
				int x, y;
				for (InterestPoint point : match.matchedPoints) {
					x = (int)point.getX();
					y = (int)point.getY();
					if (x > maxX) maxX = x;
					if (x < minX) minX = x;
					if (y > maxY) maxY = y;
					if (y < minY) minY = y;
					tempG.fillOval((int)(x * MATCH_SCALE) - 3, (int)(y * MATCH_SCALE) - 3, 6, 6);
				}

				tempG.setStroke(new BasicStroke(3f));
				x = (int)(minX * MATCH_SCALE) - 6;
				y = (int)(minY * MATCH_SCALE) - 6;
				int width = (int)((maxX - minX) * MATCH_SCALE) + 12;
				int height = (int)((maxY - minY) * MATCH_SCALE) + 12;
				tempG.setColor(new Color(0, 0, 255, 150));
				tempG.drawRect(x, y, width, height);
				tempG.setColor(new Color(0, 0, 255, 50));
				tempG.fillRect(x, y, width, height);

				tempG.dispose();
				g2d.drawImage(tempImg, null, 0, 0);
				cachedMatchedImage = tempImg;

//			lblPointsInArea.setText(match.matchedPoints.size() + "");
//			lblMatchedPoints.setText(match.matchedPoints.size() + " (" + match.matchedPoints.size() + ")");
			}
			else {
				Graphics2D g2d = (Graphics2D)pMatch.getGraphics();
				BufferedImage tempImg = (BufferedImage)createImage(MATCH_WIDTH, MATCH_HEIGHT);
				Graphics2D tempG = (Graphics2D)tempImg.getGraphics();
				tempG.drawImage(match.cachedImage, 0, 0, null);
				tempG.dispose();
				g2d.drawImage(tempImg, null, 0, 0);
				cachedMatchedImage = tempImg;

//			lblPointsInArea.setText(match.pointsInArea.size() + "");
//			lblMatchedPoints.setText(match.matchedPointsInArea.size() + " (" + match.matchedPoints.size() + ")");
			}
		}
//		lblMatchName.setText(match.imgName);
	}

	//! Draws the selection in the query image
	private void drawPolygon(int x, int y, boolean save) {
		int size = qxCoord.size();
		int[] xCoordArray, yCoordArray;
		if (!save) {
			xCoordArray = new int[size + 1];
			yCoordArray = new int[size + 1];
			xCoordArray[size] = x;
			yCoordArray[size] = y;
		}
		else {
			xCoordArray = new int[size];
			yCoordArray = new int[size];
		}

		for (int i = 0; i < size; i++) {
			xCoordArray[i] = qxCoord.get(i);
			yCoordArray[i] = qyCoord.get(i);
		}

		Polygon polygon = new Polygon(xCoordArray, yCoordArray, xCoordArray.length);

		Graphics2D g2d = (Graphics2D)pImgLoaded.getGraphics();
		BufferedImage tempImg = (BufferedImage)createImage(QUERY_WIDTH, QUERY_HEIGHT);
		Graphics2D tempG = (Graphics2D)tempImg.getGraphics();
		tempG.drawImage(originalLoadedImage, 0, 0, null);
		tempG.setStroke(new BasicStroke(4f));
		tempG.setColor(new Color(0, 0, 255, 150));
		tempG.drawPolygon(polygon);
		tempG.setColor(new Color(0, 0, 255, 50));
		tempG.fillPolygon(polygon);

		getPointsInPolygon(imgQueriedPoints, imgQueriedPointsInArea, polygon, QUERY_SCALE);
		tempG.setColor(new Color(250, 125, 0, 150));
		int xC, yC;
		for (InterestPoint point : imgQueriedPointsInArea) {
			xC = (int)point.getX();
			yC = (int)point.getY();
			tempG.fillOval((int)(xC * QUERY_SCALE) - 3, (int)(yC * QUERY_SCALE) - 3, 6, 6);
		}

		tempG.dispose();

		g2d.drawImage(tempImg, null, 0, 0);
		cachedLoadedImage = tempImg;

		if (save) {
			qclickCount = 0;
			qxCoord.clear();
			qyCoord.clear();
			bufferPolygon(polygon);
		}
	}

	//! Draws the selection in the match images
	private void mdrawPolygon(int x, int y, boolean save) {
		BestMatch match = bestMatches[selectedIndex];

		int size = mxCoord.size();
		int[] xCoordArray, yCoordArray;
		if (!save) {
			xCoordArray = new int[size + 1];
			yCoordArray = new int[size + 1];
			xCoordArray[size] = x;
			yCoordArray[size] = y;
		}
		else {
			xCoordArray = new int[size];
			yCoordArray = new int[size];
		}
		for (int i = 0; i < size; i++) {
			xCoordArray[i] = mxCoord.get(i);
			yCoordArray[i] = myCoord.get(i);
		}

		Polygon polygon = new Polygon(xCoordArray, yCoordArray, xCoordArray.length);

		Graphics2D g2d = (Graphics2D)pMatch.getGraphics();
		BufferedImage tempImg = (BufferedImage)createImage(MATCH_WIDTH, MATCH_HEIGHT);
		Graphics2D tempG = (Graphics2D)tempImg.getGraphics();
		tempG.drawImage(match.image, 0, 0, MATCH_WIDTH, MATCH_HEIGHT, null);
		tempG.setStroke(new BasicStroke(4f));
		tempG.setColor(new Color(0, 0, 255, 150));
		tempG.drawPolygon(polygon);
		tempG.setColor(new Color(0, 0, 255, 50));
		tempG.fillPolygon(polygon);

		getPointsInPolygon(match.imgPoints, match.pointsInArea, polygon, MATCH_SCALE);
		tempG.setColor(new Color(250, 125, 0, 150));
		int xC, yC;
		for (InterestPoint point : match.pointsInArea) {
			xC = (int)point.getX();
			yC = (int)point.getY();
			tempG.fillOval((int)(xC * MATCH_SCALE) - 3, (int)(yC * MATCH_SCALE) - 3, 6, 6);
		}

		tempG.dispose();

		g2d.drawImage(tempImg, null, 0, 0);
		cachedMatchedImage = tempImg;
		match.cachedImage = tempImg;

		match.getMatchedPointsInArea(polygon, MATCH_SCALE);

//		lblMatchName.setText(match.imgName);
//		lblMatchedPoints.setText(match.getMatchedPointsInArea(polygon, MATCH_SCALE).size() +
//			" (" + match.matchedPoints.size() + ")");
//		lblPointsInArea.setText(match.pointsInArea.size() + "");

		if (save) {
			mclickCount = 0;
			mxCoord.clear();
			myCoord.clear();
		}
	}

	//! Gets the interest points inside a polygon
	private List<InterestPoint> getPointsInPolygon(Collection<InterestPoint> matchedPoints,
	                                               List<InterestPoint> points) {
		int x, y, minX, minY, maxX, maxY;
		minX = 800;
		minY = 600;
		maxX = 0;
		maxY = 0;
		for (InterestPoint point : matchedPoints) {
			x = (int)point.getX();
			y = (int)point.getY();
			if (x > maxX) maxX = x;
			if (x < minX) minX = x;
			if (y > maxY) maxY = y;
			if (y < minY) minY = y;
		}
		maxX += 3; maxY += 3; minX -= 3; minY -= 3;
		Polygon polygon = new Polygon();
		polygon.addPoint(minX, minY);
		polygon.addPoint(maxX, minY);
		polygon.addPoint(maxX, maxY);
		polygon.addPoint(minX, maxY);
		List<InterestPoint> pointsInPolygon = new ArrayList<InterestPoint>();
		for (InterestPoint point : points) {
			if (polygon.contains(point.getX(), point.getY())) {
				pointsInPolygon.add(point);
			}
		}
		return pointsInPolygon;
	}

	//! Gets the interest points inside a polygon
	private void getPointsInPolygon(List<InterestPoint> ipts, List<InterestPoint> ipts2, Polygon polygon, float scale) {
		ipts2.clear();
		for (InterestPoint ipt : ipts) {
			if (polygon.contains(ipt.getX() * scale, ipt.getY() * scale)) {
				ipts2.add(ipt);
			}
		}
	}

	//! Buffers the area selected in the query image
	@SuppressWarnings({"ConstantConditions"})
	private void bufferPolygon(Polygon polygon) {
		// Remove the irrelevant pixels from the image
		bufferedPolygon = new BufferedImage(QUERY_WIDTH, QUERY_HEIGHT, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g2d = bufferedPolygon.createGraphics();
		g2d.drawImage(originalLoadedImage, 0, 0, null);
		g2d.dispose();
		int tp = new Color(0, 0, 0, 0).getRGB();
		int minX = QUERY_WIDTH, minY = QUERY_HEIGHT, maxX = 0, maxY = 0;
		for (int x = 0; x < QUERY_WIDTH; x++) {
			for (int y = 0; y < QUERY_HEIGHT; y++) {
				if (!polygon.contains(x, y)) {
					bufferedPolygon.setRGB(x, y, tp);
				}
				else {
					if (x < minX) minX = x;
					if (x > maxX) maxX = x;
					if (y < minY) minY = y;
					if (y > maxY) maxY = y;
				}
			}
		}
		bufferedPolygon = bufferedPolygon.getSubimage(minX, minY, maxX - minX, maxY - minY);
	}

	//! Saves the area selected as a .png file, matching the name of the label created
	private void savePolygon(String filename) {
		try {
			File tempFile = new File(filename);
			ImageIO.write(bufferedPolygon, "png", tempFile);
		} catch (Exception e) { e.printStackTrace(); }
	}

	//! Adds a JButton
	private void addButton(JButton button, int x, int y, int width, int height,
	                       String toolTipText, String iconName, JPanel pane, Container container) {
		Icon icon = new ImageIcon(getClass().getClassLoader().getResource("icons/" + iconName + ".png"));
		Icon rIcon = new ImageIcon(getClass().getClassLoader().getResource("icons/" + iconName + "O.png"));
		Icon pIcon = new ImageIcon(getClass().getClassLoader().getResource("icons/" + iconName + "P.png"));
		SwingUtil.addButton(button, x, y, width, height, toolTipText, icon, rIcon, pIcon, pane, container);
	}

	//! Clears the check boxes
	private void clearChbMatches() {
		for (JCheckBox chb : chbMatches) {
			chb.setSelected(false);
		}
	}

	//! Opens the annotation dialog
	private void openAnnotationDialog() {
		setEnabled(false);
		new DialogAnnotation(frmTagCreation);
	}

	//! Opens the audio recording dialog
	private void openAudioDialog() {
		setEnabled(false);
		DialogAudio dAudio = new DialogAudio(frmTagCreation);
		if (DialogAudio.tmpAudioSaved) {
//			new DialogAudio(frmTagCreation);
//		}
//		else {
//			new DialogAudio(frmTagCreation);
			dAudio.createAudioInputStream(new File(Utilities.spaceDir + "tmpAudio.wav"), true);
		}
	}

	//! Opens the calendar dialog
	private void openCalendarDialog() {
		setEnabled(false);
		new DialogNotificationType(frmTagCreation, date);
	}


	//! Saves the five kind of tags
	private void saveTag() {
		saveTag(Integer.MAX_VALUE, "all");
		saveTag(660, "660");
		saveTag(450, "450");
		saveTag(300, "300");
		saveTag(Integer.MAX_VALUE, "matched");
	}

	//! Saves the tag to the proper files
	@SuppressWarnings({"ResultOfMethodCallIgnored", "unchecked"})
	private void saveTag(int max, String tagSize) {
		//TODO: How many points?
		File tagDir = new File(Utilities.getTagDir(spaceID) + tagSize);
		tagDir.mkdirs();
		String tagID = getNextIndex(tagDir);
		File newTagDir = new File(tagDir + "\\" + tagID);
		newTagDir.mkdir();
		List<InterestPoint> pointsToAdd = new ArrayList<InterestPoint>();
		for (int i = 0; i < numberOfBestMatches; i++) {
			if (chbMatches[i].isSelected()) {
				System.out.println((i + 1) + " " + bestMatches[i].pointsInArea.size());
				if (!tagSize.equals("matched")) {
					pointsToAdd.addAll(bestMatches[i].pointsInArea);
					pointsToAdd.removeAll(bestMatches[i].matchedPointsInArea);
				}
				else {
					System.out.println("Matched in area " + bestMatches[i].matchedPointsInArea.size());
					pointsToAdd.addAll(bestMatches[i].matchedPointsInArea);
				}
			}
		}

		max -= imgQueriedPointsInArea.size();
		int size = pointsToAdd.size();
		System.out.println(size + " " + max);
		int randomIndex;

		while (size > max && max > 0) {
			randomIndex = (int)(Math.random() * size);
//			System.out.println(size + " " + randomIndex);
			pointsToAdd.remove(randomIndex);
			size--;
		}
		System.out.println(pointsToAdd.size() + " added (without imgQueriedPoints)");
		pointsToAdd.addAll(imgQueriedPointsInArea);
		System.out.println(pointsToAdd.size() + " added (with imgQueriedPoints)");

		// Save the interest points file
		Points points = new Points(pointsToAdd, spaceID);
		Utilities.saveToFile(points, newTagDir + "\\" + tagID + ".poi");
		// Save the tag image
		savePolygon(newTagDir + "\\" + tagID + ".png");
		// Save the tag annotation (if chosen)
		if (chbAnnotation.isSelected()) {
			Utilities.saveString(newTagDir + "\\" + tagID + ".txt", annotation);
		}
		// Save the tag audio clip (if chosen and exists)
		if (chbAudio.isSelected() && DialogAudio.tmpAudioSaved) {
//			Utilities.saveAudioClip(newTagDir + "\\" + tagID + ".wav");
			Utilities.copyFile(Utilities.spaceDir + "tmpAudio.wav", newTagDir + "\\" + tagID + ".wav");
		}

		//TODO: Save VIDEO

		// Save the tag date
		JSONObject joDate = (JSONObject) JSONValue.parse(date);
		joDate.put("enabled", !tbnAvailability.isSelected());
		Utilities.saveString(newTagDir + "\\" + tagID + ".date", joDate.toJSONString());

		// Add entry to changelog
		addTagChangelog(tagID);
		// Close tag creation window
		closeWindow(true);
		// Update tag thumbnails
		frmSpace.setComponents();
	}

	//! Adds a 'tag creation' entry to the changelog
	@SuppressWarnings({"unchecked"})
	private void addTagChangelog(String tagID) {
		JSONArray jaChangelog = Utilities.loadJSONArray(Utilities.spaceDir + "changelog.ans");
		JSONObject joSpace = new JSONObject();
		JSONArray jaTags = new JSONArray();
		JSONObject joTag;
		boolean spaceFound = false;
		for (Object obj : jaChangelog) {
			joSpace = (JSONObject) obj;
			if (joSpace.get("spaceID").toString().equals(spaceID) &&
					Integer.parseInt(joSpace.get("action").toString()) != Utilities.DELETED) {
				spaceFound = true;
				break;
			}
		}
		if (!spaceFound) {
			joSpace = new JSONObject();
			joSpace.put("spaceID", spaceID);
			joSpace.put("action", Utilities.NO_ACTION);
			joTag = new JSONObject();
			joTag.put("tagID", tagID);
			joTag.put("action", Utilities.CREATED);
			jaTags.add(joTag);
			joSpace.put("tags", jaTags);
			jaChangelog.add(joSpace);
		}
		else {
			jaTags = (JSONArray) joSpace.get("tags");
			boolean tagFound = false;
			for (Object obj : jaTags) {
				joTag = (JSONObject) obj;
				if (joTag.get("tagID").toString().equals(tagID) &&
						Integer.parseInt(joTag.get("action").toString()) != Utilities.DELETED) {
					tagFound = true;
					break;
				}
			}
			if (!tagFound) {
				joTag = new JSONObject();
				joTag.put("tagID", tagID);
				joTag.put("action", Utilities.CREATED);
				jaTags.add(joTag);
//				joSpace.put("tags", jaTags);
//				jaChangelog.add(joSpace);
			}
		}
		// save to the changelog file used to update the server tag repository cache
		Utilities.addToLog(spaceID, tagID, "TAGchangelog.ans");
		// save to the changelog file used to update the client tag repository
		Utilities.saveString(Utilities.spaceDir + "changelog.ans", jaChangelog.toJSONString());
	}

	//! Obtains the next tag name of the current spaceID
	//! Tag names are in the form: 001 to 999...
	@SuppressWarnings({"ConstantConditions"})
	private String getNextIndex(File dir) {
		int nextIndex = 1, fileIndex;
		FileFilter filter = new FileFilter() {
			public boolean accept(File file) {
				return file.isDirectory();
			}
		};
		File[] files = dir.listFiles(filter);
		for (File file : files) {
			try {
				fileIndex = Integer.parseInt(file.getName());
				if (nextIndex < fileIndex) {
					return Utilities.addZeros(nextIndex, 3);
				}
				else {
					nextIndex++;
				}
			} catch (Exception e) { System.out.println("Alguien estuvo jugando con los nombres de las etiquetas."); }
		}

		return Utilities.addZeros(nextIndex, 3);
	}

	public void enableTag() {
		if (tbnAvailability.isSelected()) {
			tbnAvailability.setToolTipText("Habilitar");
		}
		else {
			tbnAvailability.setToolTipText("Deshabilitar");
		}
	}


	private void actionShowMatches(){
		firstMatches = !firstMatches;
		boolean matchVisible = firstMatches;
		for (int i = 0; i < numberOfBestMatches; i++) {
			if (i == 5) {
				matchVisible = !matchVisible;
			}
			btnMatches[i].setVisible(matchVisible);
			chbMatches[i].setVisible(matchVisible);
		}
		String iconName;
		if (firstMatches) {
			iconName = "expand";
		}
		else {
			iconName = "collapse";
		}
		Icon icon = new ImageIcon(getClass().getClassLoader().getResource("icons/" + iconName + ".png"));
		Icon rIcon = new ImageIcon(getClass().getClassLoader().getResource("icons/" + iconName + "O.png"));
		Icon pIcon = new ImageIcon(getClass().getClassLoader().getResource("icons/" + iconName + "P.png"));
		btnShowMatches.setIcon(icon);
		btnShowMatches.setRolloverIcon(rIcon);
		btnShowMatches.setPressedIcon(pIcon);
	}


	public static void main(String [] a) {
		new FrameTagCreation();
	}
}

class BestMatch {
	public List<InterestPoint> imgPoints;
	public Collection<InterestPoint> matchedPoints;
	public List<InterestPoint> pointsInArea = new ArrayList<InterestPoint>();
	public List<InterestPoint> matchedPointsInArea = new ArrayList<InterestPoint>();
	public String imgName;
	public Image image, cachedImage;
	public boolean hasAreaSelected = false;

	public BestMatch(String imgName, Collection<InterestPoint> matchedPoints, Image image) {
		this.imgName = imgName;
		this.matchedPoints = matchedPoints;
		matchedPointsInArea.addAll(matchedPoints);
		this.image = image;
		imgPoints = SurfUtil.storedPOIs.get(imgName).points;
	}

	//! Gets only the matched points inside the area of a rectangle
	public List<InterestPoint> getMatchedPointsInArea(Polygon polygon, float scale) {
		matchedPointsInArea.clear();
		for (InterestPoint ipt : matchedPoints) {
			if (polygon.contains(ipt.getX() * scale, ipt.getY() * scale)) {
				matchedPointsInArea.add(ipt);
			}
		}
		return matchedPointsInArea;
	}
}